package com.jsh.erp.service.bom;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jsh.erp.datasource.entities.Bom;
import com.jsh.erp.datasource.entities.BomMaterial;
import com.jsh.erp.datasource.entities.Material;
import com.jsh.erp.datasource.entities.MaterialCategory;
import com.jsh.erp.datasource.mappers.BomMapper;
import com.jsh.erp.datasource.vo.BomEntity;
import com.jsh.erp.datasource.vo.BomVo4List;
import com.jsh.erp.exception.BusinessParamCheckingException;
import com.jsh.erp.exception.BusinessRunTimeException;
import com.jsh.erp.exception.JshException;
import com.jsh.erp.service.bomMaterial.BomMaterialService;
import com.jsh.erp.service.material.MaterialService;
import com.jsh.erp.service.materialCategory.MaterialCategoryService;
import com.jsh.erp.utils.IdUtils;
import com.jsh.erp.utils.SpringUtils;
import com.jsh.erp.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class BomService extends ServiceImpl<BomMapper, Bom> {
    private Logger logger = LoggerFactory.getLogger(BomService.class);

    @Resource
    private MaterialService materialService;

    @Resource
    private MaterialCategoryService materialCategoryService;

    @Resource
    private BomMaterialService bomMaterialService;

    @Resource
    private BomMapper bomMapper;

    public List<BomVo4List> findBom(String bomParam, String categoryId, int offset, int rows) {
        List<BomVo4List> list = null;
        try {
            List<Long> categoryIdList = new ArrayList<>();
            if (StringUtil.isNotEmpty(categoryId)) {
                categoryIdList = materialService.getListByParentId(Long.parseLong(categoryId));
            }
            list = bomMapper.findByConditionBom(bomParam, categoryIdList, offset, rows);
        } catch (Exception e) {
            JshException.readFail(logger, e);
        }
        return list;
    }

    public Long countBom(String bomParam, String categoryId) {
        Long result = 0L;
        try {
            List<Long> categoryIdList = new ArrayList<>();
            if (StringUtil.isNotEmpty(categoryId)) {
                categoryIdList = materialService.getListByParentId(Long.parseLong(categoryId));
            }
            result = bomMapper.countConditionBom(bomParam, categoryIdList);
        } catch (Exception e) {
            JshException.readFail(logger, e);
        }
        return result;
    }

    /**
     * 检查barCode 是否存在
     *
     * @param barCode
     * @return
     */
    public boolean checkExist(String barCode) {
        if (StringUtils.isEmpty(barCode)) {
            throw new BusinessParamCheckingException("验证码不能为空");
        }

        return query().eq("bar_code", barCode).count() > 0;
    }


    @Transactional(rollbackFor = Exception.class)
    public BomEntity saveBom(Bom bom, List<BomMaterial> bomMaterials) {

        if (CollectionUtils.isEmpty(bomMaterials)) {
            throw new BusinessRunTimeException("Bom物料不能为空");
        }

        for (BomMaterial bm : bomMaterials) {
            //已经存在的物料做名称冲突检查，防止同一个编码多种物料
            if (IdUtils.isValid(bm.getMaterialId())) {
                Material material = materialService.getMaterial(bm.getMaterialId());
                String materialNameInDb = material.getName();
                String materialName = bm.getName();
                if (!StrUtil.equals(StrUtil.trim(materialNameInDb), StrUtil.trim(materialName))) {
                    throw new BusinessRunTimeException("物料编码:" + bm.getBarCode() + " 存在不同物料名称 " + materialNameInDb + " | " + materialName + " ,请检查");
                }
            }
        }

        Material material = materialService.getMaterial(bom.getMaterialId());
        Long categoryId = material.getCategoryId();
        if (material == null) {
            throw new BusinessRunTimeException("生产产品的商品信息不存在:" + bom.getMaterialId());
        }

        MaterialCategory bomCategory = materialCategoryService.getMaterialCategory(categoryId);

        if (bomCategory == null) {
            throw new BusinessRunTimeException("生产产品的分类不存在:" + categoryId);
        }

        this.saveOrUpdate(bom);

        BomEntity bomEntity = new BomEntity();
        BeanUtils.copyProperties(bom, bomEntity);
        bomEntity.setCategoryId(categoryId);
        bomEntity.setCategoryName(bomCategory.getName());
        bomEntity.setName(material.getName());
        bomEntity.setModel(material.getModel());
        bomEntity.setStandard(material.getStandard());
        bomEntity.setUnit(material.getUnit());

        boolean includeMutually = bomMaterials.stream().anyMatch(m -> Objects.equals(bom.getId(), m.getMaterialId()) || Objects.equals(m.getBarCode(), bom.getBarCode()));
        if (includeMutually) {
            throw new BusinessRunTimeException("原料列表不能包含生产产品本身");
        }

        bomMaterials.forEach(bomMaterial -> {
            bomMaterial.setBomId(bomEntity.getId());
            bomMaterial.setBomName(bomEntity.getName());
            Long mId = bomMaterial.getMaterialId();

            //reset 其他属性
            //TODO 放到内存中加速查询
            Material m = materialService.getMaterial(mId);
            MaterialCategory materialCategory = materialCategoryService.getMaterialCategory(m.getCategoryId());
            bomMaterial.setName(m.getName());
            bomMaterial.setCategoryId(m.getCategoryId());
            bomMaterial.setCategoryName(materialCategory.getName());
            bomMaterial.setUnit(m.getUnit());
        });

        List<Long> postIds = bomMaterials.stream().map(m -> m.getId())
                .filter(mId -> mId != null && mId > 0)
                .collect(Collectors.toList());

        //清理是bomId的，又不在新修改的列表内的数据
        QueryWrapper<BomMaterial> deleteWrapper = new QueryWrapper();
        deleteWrapper.eq("bom_id", bom.getId());
        if (!CollectionUtils.isEmpty(postIds)) {
            deleteWrapper.notIn("id", postIds);
        }

        //删除不再存储的
        bomMaterialService.remove(deleteWrapper);
        //保存新的或者修改过的
        bomMaterialService.saveOrUpdateBatch(bomMaterials);

        bomEntity.setMaterials(bomMaterials);
        return bomEntity;
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteByBomId(Long id) {
        this.removeById(id);
        bomMaterialService.removeForBomId(id);
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteByBomIds(Collection<Long> ids) {
        Optional.ofNullable(ids).orElse(Collections.EMPTY_LIST).stream()
                .forEach(id -> deleteByBomId((Long) id));
    }

    /**
     * 获取bom 实体
     *
     * @param id
     * @return
     */
    public BomEntity getBom(Long id) {
        BomEntity bomEntity = new BomEntity();
        Bom bom = getById(id);
        BeanUtils.copyProperties(bom, bomEntity);
        List<BomMaterial> bomMaterials = bomMaterialService.listByBomId(id);
        bomEntity.setMaterials(bomMaterials);
        return bomEntity;
    }

    /**
     * 是否存在该无效关联的bom
     *
     * @param materialIds
     * @return
     */
    public boolean existByMaterialId(Collection<Long> materialIds) {
        return count(new QueryWrapper<Bom>().in("material_id", materialIds)) > 0;
    }

    /**
     * 从excel导入，会关闭流
     *
     * @param excelInputStream
     */
    @Transactional(rollbackFor = Exception.class)
    public void importForExcel(InputStream excelInputStream) {
//        ExcelReader reader = ExcelUtil.getReader(excelInputStream, 0);
        BomImportRowHandler bomImportRowHandler = SpringUtils.getBean(BomImportRowHandler.class);
        ExcelUtil.readBySax(excelInputStream, 0, bomImportRowHandler);
        IoUtil.close(excelInputStream);
    }

    /**
     * barCode 查找
     *
     * @param barCode
     * @return
     */
    public List<Bom> findByBarCode(String barCode) {
        return query().eq("bar_code", barCode).list();
    }

    /**
     * barCode 查找
     *
     * @param barCode
     * @return
     */
    public List<Bom> findByBarCodes(Collection<String> barCode) {
        return query().in("bar_code", barCode).list();
    }
}
